/*******************************************************************************
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Olexandr V. Shaposhnikov - initial API and implementation and/or initial documentation
 *******************************************************************************/

grammar UnrealScript;

options {
   language = Java;
   backtrack=true;
   memoize=true;
   
}

@lexer::header {
package com.funkyroach.use.core.parser.antlr;
import com.funkyroach.use.core.ast.USMacro;

}

@parser::header {
package com.funkyroach.use.core.parser.antlr;

import java.util.ArrayList;

import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.ASTListNode;
import org.eclipse.dltk.ast.Modifiers;
import org.eclipse.dltk.ast.DLTKToken;
import org.eclipse.dltk.ast.statements.*;
import org.eclipse.dltk.ast.expressions.*;
import org.eclipse.dltk.ast.references.*;
import org.eclipse.dltk.ast.declarations.*;

import static org.eclipse.dltk.ast.Modifiers.*;
import static com.funkyroach.use.core.ast.IUSASTConstants.*;

import com.funkyroach.use.core.parser.USToken;
import com.funkyroach.use.core.ast.*;
import com.funkyroach.use.core.ast.expr.*;

}


@lexer::members {
private Token lastCodeToken;
protected Token lastToken;

public Token nextToken()
{
  Token result = super.nextToken();
  lastToken = result;
  if (result.getChannel() == Token.DEFAULT_CHANNEL) {
    lastCodeToken = result;
  }
  return result;    
}

public USMacro getMacro(String macroName) {
  return null;
}

public void handleMacro(USMacro macro) {

}
}

@parser::members {

protected int endOf(Token t1){return 0;}
protected int startOf(Token t1){return 0;}

public void check(Token t, int i) throws MismatchedTokenException{if(t == null) throw new MismatchedTokenException(i, input);}
public void reportError(RecognitionException re) {}
public void reportThrowable(Throwable th) {}
protected USExpression createBinaryExpression(USExpression astLeftExpr, USToken kind, USExpression astRightExpr){ return null;}
protected void handlePPInclude(String path){}
protected void handlePPIf(USToken tk, USToken tkId) {}

}

@rulecatch {
catch (RecognitionException re) {
  reportError(re);
  recover(input,re);
}
catch (Throwable th) {
    reportThrowable(th);
}

}

us [USModuleDeclaration moduleDecl]: 
  ( astClassDef = classDef astClassBody  = classBody )? tk_eof = EOF 
{
  moduleDecl.setClassDeclaration(astClassDef);
  if(astClassDef != null) {
    astClassDef.setBody(astClassBody);
    astClassBody.setEnd(endOf(tk_eof));
  }
  moduleDecl.setEnd(endOf(tk_eof));
};


//-----------------------------------------------------------------------------
// CLASS
//-----------------------------------------------------------------------------
classDef returns [USClassDeclaration classDecl = null]: 
    t_cType = (CLASS |INTERFACE) 
    astId = identifier  
     ( EXTENDS astSuperId = identifier )? 
     classModifier* tkSemi = SEMICOLON
{
  classDecl = new USClassDeclaration(astId);
  if(astSuperId != null){
    classDecl.addSuperClass(new USTypeReference(astSuperId));
  }
  classDecl.setStart(startOf(t_cType));
  classDecl.setEnd(endOf(tkSemi));
};
    
    
    
classModifier:
    ABSTRACT 
    | DEPRECATED
    | NATIVE (LBRACK identifier RBRACK)?
    | NATIVEREPLICATION
    | PLACEABLE
    | NOTPLACEABLE
    | INHERITS (LBRACK identifierList RBRACK)? 
    | AUTOEXPANDCATEGORIES (LBRACK identifierList RBRACK)
    | IMPLEMENTS(LBRACK identifierList RBRACK)
    | CLASSGROUP (LBRACK identifierList RBRACK)
    | SAFEREPLACE
    | PEROBJECTCONFIG
    | TRANSIENT
    | NOEXPORT
    | HIDEDROPDOWN
    | EDITINLINENEW
    | (COLLAPSECATEGORIES |DONTCOLLAPSECATEGORIES)
    | EXPORTSTRUCTS
    | WITHIN identifier
    | DEPENDSON (LBRACK identifierList RBRACK)
    | CONFIG ( LBRACK identifier RBRACK )?
    | HIDECATEGORIES LBRACK identifierList RBRACK
    | FORCESCRIPTORDER LBRACK BoolValue RBRACK
    | SHOWCATEGORIES LBRACK identifierList RBRACK
    ;

classBody returns [USClassBody astBody = new USClassBody()]:
     ( (constDef) => constDef 
    | (enumDef) => enumDef
    | astVarsDecl = varsDecl {astBody.addStatement(astVarsDecl);}
    | (STRUCT) => astStruct = structDef {astBody.addStatement(astStruct);}
    | (operatorDef) => operatorDef
    | astFuncDef=funcDef  {astBody.addStatement(astFuncDef);}
    | stateDef
    | (replicationDef) => replicationDef
    | (cppText) => cppText
    | astDefProp  = defaultProperties {astBody.addStatement(astDefProp);}
    | ppStatement
    | (delegateDef) => delegateDef
    | SEMICOLON 
    )* 
{
//XXX TODO !!!!!
};
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//CONST
//-----------------------------------------------------------------------------
constDef : CONST identifier '=' expression SEMICOLON;
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//VAR
//-----------------------------------------------------------------------------
varsDecl returns[ASTListNode astVarsDecl = new ASTListNode()]
   :
      (LOCAL? tk_var = VAR (LBRACK identifierList? RBRACK)? | LOCAL) 
      varSpecifiers  
      (typeRef)=> astTypeRef =typeRef 
      astDeclNames = varNameList 
      tkSemi = SEMICOLON
{
for (USDeclaration decl : astDeclNames) {
  if(decl != null) {
    USVariableDeclaration varDecl = new USVariableDeclaration(decl);
    varDecl.setType(astTypeRef);
    varDecl.setStart(startOf(tk_var));
    varDecl.setEnd(endOf(tkSemi));
    astVarsDecl.addNode(varDecl); 
  }
}
  astVarsDecl.setStart(startOf(tk_var));
  astVarsDecl.setEnd(endOf(tkSemi));
  
};
    
varNameList  returns[List<USDeclaration> astVarsDeclList = new ArrayList<USDeclaration>()] 
               : astDecl = varDecl{astVarsDeclList.add(astDecl);} (COMMA astDecl = varDecl{astVarsDeclList.add(astDecl);} )*;

varDecl returns [USDeclaration astDecl = null]
               : t_name = identifier constArrayIndex* metadata? nativeInfo? 
{ 
  astDecl = new USDeclaration(t_name);
};

varSpecifiers  : 
               ( CONFIG    //This variable will be made configurable. The current value can be saved to the ini file and will be loaded when created. Cannot be given a value in default properties. Implies const.
               | GLOBALCONFIG //Works just like config except that you can't override it in a subclass. Cannot be given a value in default properties. Implies const.  
               | LOCALIZED //{modifiers|=USModifiers.LOCALIZED;} //The value of this variable will have a localized value defined. Mostly used for strings. Implies const. Read more about this in the Localization Reference and Unreal Strings.  
               | CONST //{modifiers|=Modifiers.AccConst;} //Treats the contents of the variable as a constant. In UnrealScript, you can read the value of const variables, but you can't write to them. "Const" is only used for variables which the engine is responsible for updating, and which can't be safely updated from UnrealScript, such as an actor's Location (which can only be set by calling the MoveActor function).     
               |
                 ( PRIVATE // {modifiers|=Modifiers.AccPrivate;} //The variable is private, and may only be accessed by the class's script; no other classes (including subclasses) may access it.    
                  | PROTECTED //{modifiers|=Modifiers.AccProtected;} //The variable can only be accessed from the class and it's subclasses, not from other classes.
                  | PUBLIC //{modifiers|=Modifiers.AccPublic;}
                 ) ('{' (PRIVATE| PUBLIC| PROTECTED) '}')?
               | REPNOTIFY //Actors should be notified (via the ReplicatedEvent function) when this value for this property is received via replication. 
               | DEPRECATED //Indicates that this variable is going to be removed in the near future, and should no longer be accessible in the editor. Deprecated properties are loaded, but not saved.  
               | INSTANCED //Object properties only. When an instance of this class is created, it will be given a unique copy of the object assigned to this variable in defaults. Used for instancing subobjects defined in class default properties.
               | DATABINDING //This property can be manipulated by the data store system.
               | EDITORONLY //    This property's value will only be loaded when running UnrealEd or a commandlet. During the game, the value for this property is discarded.
               | NOTFORCONSOLE //This property's value will only be loaded when running on the PC. On consoles, the value for this property is discarded.   
               | EDITCONST //Editor. The variable can be seen in UnrealEd but not edited. A variable that is editconst is not implictly "const".
               | EDITFIXEDSIZE //Editor. Only useful for dynamic arrays. This will prevent the user from changing the length of an array via the UnrealEd property window.    
               | EDITINLINE //Editor. Allows the user to edit the properties of the object referenced by this variable within UnrealEd's property inspector (only useful for object references, including arrays of object reference).  
               | EDITINLINEUSE //Editor. In addition to the behavior associated with editinline, adds a "Use" button next to this object reference in the editor
               | EDITHIDE //
               | NOCLEAR //Editor. Allows this object reference to be set to None from the editor.
               | INTERP // Editor. Indicates that the value can be driven over time by a Float or Vector Property Track in Matinee.
               | INPUT //Advanced. Makes the variable accessible to Unreal's input system, so that input (such as button presses and joystick movements) can be directly mapped onto it. Only relevent with variables of type "byte" and "float".    
               | TRANSIENT //Advanced. Declares that the variable is for temporary use, and isn't part of the object's persistent state. Transient variables are not saved to disk. Transient variables are initialized to the class's default value for that variable when an object is loaded.
               | DUPLICATETRANSIENT //Advanced. Indicates that the variable's value should be reset to the class default value when creating a binary duplicate of an object (via StaticDuplicateObject).
               | NOIMPORT //Advanced. Indicates that this variable should be skipped when importing T3D text. In other words, the value of this variable will not be transferred to new object instances when importing or copy/pasting objects.   
               | NATIVE //Advanced. Declares that the variable is loaded and saved by C++ code, rather than by UnrealScript.      
               | EXPORT //Advanced. Only useful for object properties (or arrays of objects). Indicates that the object assigned to this property should be exported in its entirety as a subobject block when the object is copied (for copy/paste) or exported to T3D, as opposed to just outputting the object reference itself.
               | NOEXPORT //Advanced. Only useful for native classes. This variable should not be included in the auto-generated class declaration.
               | NONTRANSACTIONAL  //Advanced. Indicates that changes to this variable value will not be included in the editor's undo/redo history.
               | INIT //Advanced. This property should be exported to the header file as an FString or TArray, rather than an FStringNoInit or TArrayNoInit. Only applicable to strings and dynamic arrays declared in native classes. 'Init' properties should not be given default values, as the default value will be cleared when the object is created. (See Unreal Strings and Native Strings)
               | PROTECTEDWRITE // http://wiki.beyondunreal.com/ProtectedWrite#Access_modifiers
               | REPRETRY //Advanced; struct properties only. Retry replication of this property if it fails to be fully sent (e.g. object references not yet available to serialize over the network). For simple references this is the default, but for structs this is often undesirable due to the bandwidth cost, so it is disabled unless this flag is specified.
               )*;
//-----------------------------------------------------------------------------

operatorDef:
  NATIVE? ( '(' IntValue ')' )? STATIC FINAL 
  ( (OPERATOR) => (OPERATOR ( '(' IntValue ')' )? (typeRef) operator operatorArgs block?)
  | (PREOPERATOR) => (PREOPERATOR  typeRef preOperator LBRACK operatorArg RBRACK)
  | (POSTOPERATOR) =>(POSTOPERATOR  typeRef postOperator LBRACK operatorArg RBRACK)
  );
operatorArgs: LBRACK (operatorArg (COMMA operatorArg)*)? RBRACK;
operatorArg: ( OUT| COERCE|CONST |INIT| SKIP)*  typeRef varDecl ('=' expression)?;
//-----------------------------------------------------------------------------
//FUNC
//-----------------------------------------------------------------------------
funcDef returns [USFuncDeclaration astFuncDecl = null] options {greedy=true;}
:  
           funcModifiers tk_start = (FUNCTION|EVENT) funcModifiers  astTypeRef= typeRef?  
           (identifier LBRACK)=> ( 
           astId = identifier 
           astArgList = funcArgs CONST? ( (SEMICOLON)=> tkSemicolon = SEMICOLON | astBlock = funcBody))
{
  astFuncDecl = new USFuncDeclaration(astId);
  astFuncDecl.setReturnType(astTypeRef);
  astFuncDecl.setStart(startOf(tk_start));
  astFuncDecl.acceptArguments(astArgList);
  if(astBlock != null) {
    astFuncDecl.setEnd(astBlock.sourceEnd());
    astFuncDecl.acceptBody(astBlock);
  } else {
    astFuncDecl.setEnd(endOf(tkSemicolon));
  }
};

funcBody returns [USFuncBody astBody = null]
:
{astBody = new USFuncBody();} 
tk_LCBRACK =LCBRACK astStatements = statements  tk_RCBRACK = RCBRACK 
{
  astBody.addStatement(astStatements);
  astBody.setStart(startOf(tk_LCBRACK));
  astBody.setEnd(endOf(tk_RCBRACK));
};

funcArg returns [USArgument astArg = null]: 
( OPTIONAL| OUT| COERCE|CONST |INIT)* astTypeRef = typeRef astVarDecl=varDecl ('=' astExpression =expression)?
{
  astArg = new USArgument(astVarDecl);
  astArg.setInitializationExpression(astExpression);
  astArg.setType(astTypeRef);
  astArg.setStart(astVarDecl.sourceStart());
  astArg.setEnd(astVarDecl.sourceEnd());
};


funcArgs returns[List<USArgument> astArgList = null]:
{astArgList = new ArrayList<USArgument>();}  
'(' (
      (
        astArg = funcArg {astArgList.add(astArg);} COMMA
      )* 
    astArg = funcArg {astArgList.add(astArg);}
    )? ')'
;

funcModifiers
: (
   (K2PURE|K2CALL)
  | FINAL 
  | ITERATOR
  | COERCE
  | LATENT //Declares that a native function is latent, meaning that it can only be called from state code, and it may return after some game-time has passed. 
  | NOEXPORT //Only used for native functions. Declares that the C++ function declaration for this native function should not be exported. Only the declaration for the glue version of the function will be exported.
  | SIMULATED 
  | SINGULAR 
  | STATIC 
  | NOEXPORTHEADER
  | EXEC //Indicates that this function can be executed by typing the name of the function into the console. Only valid in certain classes.
  | (PROTECTED | PRIVATE) 
  | (RELIABLE | UNRELIABLE)
  | (CLIENT | SERVER)
  | VIRTUAL //Unlike languages like C++, static functions are virtual and can be overridden in child classes. 
  | NATIVE ( LBRACK IntValue RBRACK )?
  )*;
//-----------------------------------------------------------------------------

delegateDef : DELEGATE typeRef? identifier funcArgs funcBody?;

//=============================================================================
// State
//=============================================================================
replicationDef: REPLICATION LCBRACK replicationStatement*  RCBRACK;
replicationStatement: IF LBRACK expression RBRACK identifierList SEMICOLON;

//=============================================================================
// State
//=============================================================================
stateDef  returns [USStateDeclaration astStateDecl = null] 
                    :  stateParams? 
                       STATE  
                       astId = identifier
                       stateBody
{
  astStateDecl = new USStateDeclaration(astId);
};

stateBody returns [USStateBody astStatebody = null]          
           : LCBRACK stateIgnores? 
            (funcDef | stateCode)*
           RCBRACK 
{
  astStatebody = new USStateBody();
};
                      
stateCode           : (IDENTIFIER | BEGIN) COLON stateCodeStatement*;
stateCodeStatement  : expression SEMICOLON;
stateIgnores        : IGNORES identifierList SEMICOLON;
stateParams         : AUTO | SIMULATED;
//-----------------------------------------------------------------------------
//BLOCK
//-----------------------------------------------------------------------------
block :   LCBRACK statement* RCBRACK;
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//STATEMENT
//-----------------------------------------------------------------------------
statements returns [ASTListNode astStatements = new ASTListNode();]
          : (astStatement = statement {astStatements.addNode(astStatement);})*;

statement returns [ASTNode astStatement = null]
          :
            (LCBRACK) => block
          | (LOCAL) => astVarDecl = varsDecl {astStatement = astVarDecl;}
          | (VAR? ENUM) => enumDef
          | (LCBRACK) => block
          | (IF) => ifStatement
          | (RETURN) => returnStatement
          | (FOR) => forStatement
          | (FOREACH) => foreachStatement
          | (SWITCH) => switchStatement
          | (DO) => doStatement
          | (WHILE) => whileStatement
          | (BREAK) => breakStatement
          | (SEMICOLON) => SEMICOLON
          | ppStatement
          | astExpression = expression { astStatement = astExpression;} SEMICOLON
          ;


ifStatement     : IF complexExpression statement ((ELSE)=> ELSE  statement)?;
doStatement     : DO statement UNTIL LBRACK expression RBRACK;
forStatement    : FOR LBRACK expression? SEMICOLON  expression? SEMICOLON expression? RBRACK statement;
foreachStatement: FOREACH identifier (('.') => '.' identifier)* '(' ((classType ',') => classType ',')? expressionList ')' statement;
whileStatement  : WHILE LBRACK expression  RBRACK statement;
returnStatement : RETURN expression? SEMICOLON;
breakStatement  : BREAK SEMICOLON;

switchStatement : SWITCH LBRACK (expression) RBRACK LCBRACK(switchBlockStatementGroup )* RCBRACK;
switchBlockStatementGroup : switchLabel (statement)* ;
switchLabel     :  (CASE|DEFAULT) ((LBRACK (literal | identifier) RBRACK) |  (literal | identifier))? COLON;

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// ENUM
//-----------------------------------------------------------------------------
enumDef  : VAR?  enumType identifierList? SEMICOLON;
enumType : ENUM identifier LCBRACK enumValueDef (',' enumValueDef)* COMMA? RCBRACK;
enumValueDef: identifier metadata?;
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// STRUCT
//-----------------------------------------------------------------------------
structDef returns [USStructDeclaration astStructDecl= null]: 
    tk_struct = STRUCT nativeInfo? structSpec* 
    astId = identifier (EXTENDS astSuperId = identifier)?
    astBody = structBody
{
    astStructDecl = new USStructDeclaration(astId);
    astStructDecl.setStart(startOf(tk_struct));
    if(astSuperId != null) {
      astStructDecl.addSuperClass(new USTypeReference(astSuperId));
    }
    astStructDecl.setEnd(astBody.sourceEnd());
    astStructDecl.setBody(astBody);
};
    
structBody returns [Block astBody = new Block()]:
    tkStart = LCBRACK  (structCppText | structCppDefaultProperties | astVarsDecl = varsDecl {astBody.addStatement(astVarsDecl);})*  tkEnd = RCBRACK
{
  astBody.setStart(startOf(tkStart));
  astBody.setEnd(endOf(tkEnd));
};    
            
structSpec : IMMUTABLEWHENCOOKED| IMMUTABLE| ATOMIC | TRANSIENT|  NATIVE |STRICTCONFIG | EXPORT;
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// METADATA
//-----------------------------------------------------------------------------
metadata           : LABRACK metadataProperty ('|' metadataProperty )* RABRACK;
metadataProperty   : identifier('=' metadataValue)?;
metadataValue      :  ~('>'| '|')*;
nativeInfo         : LCBRACK ~(RCBRACK)* RCBRACK;
//-----------------------------------------------------------------------------
typeRef           returns [TypeReference astTypeRef = null]
                  : (ARRAY LABRACK ) => tkDynamicArrayType = dynamicArrayType { astTypeRef = tkDynamicArrayType;}
                  | (basicType) => tkBasicType = basicType { astTypeRef = tkBasicType;}
                  | t_ct =  classType { astTypeRef= t_ct;}
                  | enumType
                  | (identifier '.')? astId = identifier  { astTypeRef= new USTypeReference(astId);}
                  | DELEGATE '<' astId =identifier {astTypeRef = new USTypeReference(astId);} '>' 
                  | MAP nativeInfo?
;
                  
//embeddedType      returns[TypeReference astTypeRef = null]
//: 
//    astBasicType = basicType {astTypeRef = astBasicType;} 
//  | astEmbeddedType = embeddedClass {astTypeRef = astBasicType;}
//;

basicType         returns[TypeReference astTypeRef = null]
  : BYTE {astTypeRef = USTypeReference.BYTE_TYPE;} 
  | INT {astTypeRef = USTypeReference.INT_TYPE;}
  | FLOAT {astTypeRef = USTypeReference.FLOAT_TYPE;}
  | STRING {astTypeRef = USTypeReference.STRING_TYPE;}
  | BOOL {astTypeRef = USTypeReference.BOOL_TYPE;}
  | NAME {astTypeRef = USTypeReference.NAME_TYPE;}
  ;

dynamicArrayType  returns [TypeReference tr = null] : ARRAY LABRACK (CONST| NATIVE| TRANSIENT)* astTypeRef = typeRef {((USTypeReference) astTypeRef).setArray(true); tr = astTypeRef;} RABRACK;

delegateType      : DELEGATE LABRACK identifier RABRACK;
classType      returns [USTypeReference tk = null]   
  : (CLASS LABRACK) => CLASS LABRACK t_t1 = identifier {tk = new USTypeReference(t_t1);} RABRACK 
  | CLASS ( LCBRACK (identifier '.')? t_t2 = identifier {tk = new USTypeReference(t_t2);} RCBRACK 
  | t_t3 = ConstName {tk = new USTypeReference((USToken)t_t3);});


//-----------------------------------------------------------------------------
// Identifier
//-----------------------------------------------------------------------------
//identifier  returns [Token tk = null] options{k=1;}: {true}?  => t_tk =  IDENTIFIER {tk = t_tk;};

identifier  returns [USToken tk = null] :  
id = (
IDENTIFIER 
| MAP
| POINTER
| INTERFACE
| THIS 
| SUPER 
| DEFAULT 
| SWITCH 
| CLASS
| NAME 
| INIT 
| INPUT
| EVENT
| STRING
| AUTO
| STATIC
| END
| FUNCTION
| STATE
| PROPERTY
) {tk = (USToken)id;};

identifierList           : identifier (COMMA identifier)*;


expression  returns [Expression astExpr = null;]    
: 
  astLeftExpr = conditionalExpr  {astExpr = astLeftExpr;}
  (
    (assignmentOp)=> astKind   = assignmentOp 
    astRightExpr = expression
    {
     astExpr =  new USBinaryExpression(astLeftExpr, astKind, astRightExpr);
    } 
  )?
;


complexExpression returns [USComplexExpression astComplexExpr= null]
: LBRACK astExpr = expression RBRACK;
         
expressionList    returns [ASTListNode astList = new ASTListNode()]        
: (astExpr = expression {astList.addNode(astExpr);})? 
  (COMMA) => (COMMA (astExpr = expression? {astList.addNode(astExpr);})?
  )* ;

conditionalExpr returns [Expression astExpr = null] 
:
  astLogExpr = logicalExpr {astExpr = astLogExpr;} 
  ( '?' 
    astTrueExpression = expression ':' 
    astFalseExpression = expression
    { astExpr = new USTernaryExpression(astLogExpr, astTrueExpression, astFalseExpression);}
   )?
;
         
logicalExpr  returns [Expression astExpr = null] 
:
  astLeftExpr = shiftExpression  { astExpr= astLeftExpr;}
  (
    astKind =  boolOp 
    astRightExpr = expression
    { astExpr =  new USBinaryExpression(astLeftExpr, astKind, astRightExpr); }
  )?
;
        
shiftExpression returns [Expression astExpr = null]
: 
  astLeftExpr =  additiveExpr { astExpr= astLeftExpr;}
  (
    astKind = shiftOp 
    astRightExpr = additiveExpr
    { astExpr =  new USBinaryExpression(astLeftExpr, astKind, astRightExpr); }
  )?
;
         
additiveExpr returns [Expression astExpr = null]
: 
  astLeftExpr =  multiplicativeExpr { astExpr= astLeftExpr;}
  (
    astKind = aditiveOp 
    astRightExpr = additiveExpr
    { astExpr =  new USBinaryExpression(astLeftExpr, astKind, astRightExpr); }
  )?
;

multiplicativeExpr returns [ Expression astExpr = null]
:
 astLeftExpr = unaryExpr {astExpr = astLeftExpr;} 
 ( 
  astKind = multiplicOp 
  astRightExpr = multiplicativeExpr
  { astExpr =  new USBinaryExpression(astLeftExpr, astKind, astRightExpr); } 
 )? 
;
         
unaryExpr returns [ Expression astExpr = null]
: ((preOperator) => astKind = preOperator)? expr = postfixExpr 
  {
    if(astKind != 0) {
        astExpr = new USUnaryExpression(astKind, expr, true); 
      } else {
        astExpr = expr;
      }
  }
;

postfixExpr   returns [ Expression astExpr = null]
:   astPrimExpr = primaryExpr ( astNodeList = pointedExpr)? postOperator? 
{
  if(astPrimExpr!= null && astNodeList != null) {
    USPointedExpression subExpr = null; 
    for(astNodeList) {
      subExpr = 
    }
    astExpr = new USPointedExpression(astPrimExpr, astNodeList);
  } else { astExpr = astPrimExpr;}
};

pointedExpr returns [ASTListNode astList = null]
: ( '.' astNode = primaryExpr {if(astList == null) { astList = new ASTListNode();}astList.addNode(astNode);})*
; 

primaryExpr   returns [Expression astExpr = null]          
      : 
      classType callArgumentList? 
      | (NEW) => newExpr
      | (basicType LBRACK) => basicType complexExpression
      | (LBRACK) => complexExpression
      | (PROPERTY) => propertyRef
      | (literal) => astLiteral = literal {astExpr = astLiteral;}
      | ('`') => ppMacro
      | (identifier callArgumentList) => tkId = identifier astArgs = callArgumentList { astExpr = new USMethodCallExpression(null, tkId ,astArgs);}
      | tkId = identifier arrayIndex? {astExpr = new USVarReference(tkId);}
      | CLASS
      | CONST
      | GLOBAL
      | STATIC
      | DEFAULT
     ;

propertyRef returns [USPropertyReference expression = null;]: PROPERTY ConstName;

classRef : CLASS ConstName;
newExpr:
       NEW ( '(' fullClassName (',' StringValue)?  (',' IntValue)?  ')'
       )?  (identifier | classRef ((RBRACK)=> RBRACK identifier LBRACK)?);

fullClassName: identifier ('.' identifier)?;

callArgumentList returns [USCallArgumentsList astArgList = null]: 
  tkStart=LBRACK strExprList = expressionList? tkEnd=RBRACK
{
  astArgList = new USCallArgumentsList(
    endOf(tkStart),
    startOf(tkEnd), 
    strExprList);
};


//-----------------------------------------------------------------------------
// CPP
//-----------------------------------------------------------------------------
cppText            : CPPTEXT cppBody;
structCppText      : STRUCTCPPTEXT cppBody;
structCppDefaultProperties : STRUCTDEFAULTPROPERTIES LCBRACK defaultProperty* SEMICOLON? RCBRACK;
cppBody  :  LCBRACK  (cppSkip|cppBody)*  RCBRACK;
cppSkip  : '#' | ~(LCBRACK| RCBRACK);
       
       
//-----------------------------------------------------------------------------
// defaultProperties
//-----------------------------------------------------------------------------
defaultProperties  returns [USDefaultProperties astDefaultProperties = new USDefaultProperties()]
:  
  tkStart = DEFAULTPROPERTIES LCBRACK
    astProps = defaultPropertyList 
  tkEnd = RCBRACK
{
  astDefaultProperties.setStart(startOf(tkStart));
  astDefaultProperties.setEnd(endOf(tkEnd));
  astDefaultProperties.addNode(astProps);
}
;

defaultProperty returns [USDefaultProperty astDefProp = null]
    : 
    tkId = identifier  (callArgumentList|constArrayIndex|constPropArrayIndex)? '=' astExpr = defaultValue 
{
  astDefProp = new USDefaultProperty();
  astDefProp.setReference(new USVarReference(tkId));
  astDefProp.setValue(astExpr);
};


defaultPropertyList returns [ASTListNode propList = new ASTListNode()]
  : (
          (BEGIN) => (astDefSubObj = objectInitValue {propList.addNode(astDefSubObj);})     
        | (defaultProperty) => (astDefProp = defaultProperty {propList.addNode(astDefProp);} SEMICOLON?  (',' astDefProp = defaultProperty {propList.addNode(astDefProp);})* )
    )* ;

objectInitValue returns [USObjectInitValue astDefaultSubObject = null] 
:
  BEGIN astTypeRef = typeRef  (CLASS '=' astClassRef = identifier)? NAME '=' identifier
    defaultPropertyList 
  END astEndTypeRef = typeRef 
{

}
;

structInitValue
  : LBRACK identifier '=' preOperator? literal ( ',' identifier '=' literal)* RBRACK
  ;

defaultObjectRef returns [USObjectInitValueRef astObjectInitValueRef = null]
  : astType = typeRef astId = ConstName
{
  astObjectInitValueRef = new USObjectInitValueRef(astType, (USToken)astId);
};
 
defaultValue returns [Expression expr = null]
  :  preOperator? astLiteral = literal {expr = astLiteral;} 
  |  astDefaultObject = defaultObjectRef {expr = astDefaultObject;}
  |  structInitValue
  |  classRef
  |  identifier // enum value
  ;


elementValue : LBRACK elemValueList? RBRACK;
elemValueList: arrayElemValue (',' arrayElemValue)*;
arrayElemValue : literal | classType | identifier '=' ( (LBRACK) => elementValue | literal | identifier);
constArrayIndex : LSBRACK IntValue RSBRACK;
constPropArrayIndex : LBRACK IntValue RBRACK;
arrayIndex  returns [ USArrayIndex arrayIndex = null] : LSBRACK expression RSBRACK;
//-----------------------------------------------------------------------------
// preprocessor
//-----------------------------------------------------------------------------
ppStatement 
      : (PP_IF) => ppIfStatement
      | (PP_DEFINE) => ppDefine
      | (PP_INCLUDE) => ppInclude
      | ppMacro;  
      
ppIfStatement:  (PP_IF) => ppIf statement* (PP_ELSE statement*)? PP_ENDIF ;
ppDefineArgs : '(' identifierList ')';
ppDefine: PP_DEFINE identifier ppDefineArgs? ~('\n' | '\r')* ;
//TODO ppMacro:;
ppIf :   (PP_IF) => PP_IF LBRACK tk = (PP_ISDEFINED|PP_NOTDEFINED)  LBRACK tkId = identifier  RBRACK RBRACK
    {handlePPIf((USToken)tk, tkId);};


ppMacro : PP_MACRO  ((LBRACK) => callArgumentList)?;
ppInclude : tk = PP_INCLUDE { handlePPInclude(tk.getText()); }';';


literal    returns [ USLiteral tkLiteral = null;]  
            : 
              (FloatValue) => tk = FloatValue  {tkLiteral = new USLiteral(US_AST_FLOAT_LITERAL, (USToken)tk); }
            | (ConstName) =>  tk = ConstName {tkLiteral = new USLiteral(US_AST_CONST_LITERAL, (USToken)tk); }
            | (StringValue) =>  tk = StringValue {tkLiteral = new USLiteral(US_AST_STRING_LITERAL, (USToken)tk); }
            | (BoolValue) =>  tk = BoolValue {tkLiteral = new USLiteral(US_AST_BOOL_LITERAL, (USToken)tk); }
            | (NONE) =>  tk = NONE {tkLiteral = new USLiteral(US_AST_NONE_LITERAL, (USToken)tk); }
            | (IntValue) => tk = IntValue {tkLiteral = new USLiteral(US_AST_INT_LITERAL, (USToken)tk); }
            ;



//=============================================================================
// ALPHABET
//=============================================================================

fragment A:('a'|'A');
fragment B:('b'|'B');
fragment C:('c'|'C');
fragment D:('d'|'D');
fragment E:('e'|'E');
fragment F:('f'|'F');
fragment G:('g'|'G');
fragment H:('h'|'H');
fragment I:('i'|'I');
fragment J:('j'|'J');
fragment K:('k'|'K');
fragment L:('l'|'L');
fragment M:('m'|'M');
fragment N:('n'|'N');
fragment O:('o'|'O');
fragment P:('p'|'P');
fragment Q:('q'|'Q');
fragment R:('r'|'R');
fragment S:('s'|'S');
fragment T:('t'|'T');
fragment U:('u'|'U');
fragment V:('v'|'V');
fragment W:('w'|'W');
fragment X:('x'|'X');
fragment Y:('y'|'Y');
fragment Z:('z'|'Z');

//=============================================================================
// Built-in types 
//=============================================================================
BoolValue   : T R U E | F A L S E;
IntValue    : (DIGIT+ | ( HEX_PREFIX  HEX_DIGIT+ ));
FloatValue  : (DIGIT+ '.') => DIGIT+ '.' DIGIT* F? 
            | DIGIT* '.' DIGIT+ F?;
StringValue : DQUOTE (( '\\"') | ~DQUOTE)* DQUOTE;
ConstName   : SQUOTE (~SQUOTE)* SQUOTE;




//=============================================================================
// Keywords
//=============================================================================
PP_IF         : '`' I F;
PP_ELSE       : '`' E L S E;
PP_ENDIF      : '`' E N D I F;
PP_DEFINE     : '`' D E F I N E;
PP_ISDEFINED  : '`' I S D E F I N E D;
PP_NOTDEFINED : '`' N O T D E F I N E D;
PP_INCLUDE    : '`' I N C L U D E '(' (~')')* ')';

PP_MACRO      : '`' tk=IDENTIFIER 
                    { USMacro _macro = getMacro(tk.getText());
                      if( _macro== null ){
                      $channel = HIDDEN;
                      } else {
                        handleMacro(_macro);
                      }
                    };

ARRAY        : A R R A Y ;
MAP          : M A P;
ATOMIC       : A T O M I C;
ATOMICWHENCOOKED: A T O M I C W H E N C O O K E D;
AUTOEXPANDCATEGORIES: A U T O E X P A N D C A T E G O R I E S;
IMMUTABLE    : I M M U T A B L E;
IMMUTABLEWHENCOOKED: I M M U T A B L E W H E N C O O K E D;
STRICTCONFIG : S T R I C T C O N F I G;
NONE         : N O N E;
STATE        : S T A T E;
EXTENDS      : E X T E N D S;
INTERFACE    : I N T E R F A C E;
CLASS        : C L A S S;
CLASSGROUP   : C L A S S G R O U P;
PROPERTY     : P R O P E R T Y;
PEROBJECTCONFIG : P E R O B J E C T C O N F I G;
EXPORTSTRUCTS : E X P O R T S T R U C T S;
SHOWCATEGORIES: S H O W C A T E G O R I E S;
FORCESCRIPTORDER: F O R C E S C R I P T O R D E R;
FUNCTION     : F U N C T I O N;
EVENT        : E V E N T;
DELEGATE     : D E L E G A T E;
DUPLICATETRANSIENT : D U P L I C A T E T R A N S I E N T;
CONFIG       : C O N F I G;
CONST        : C O N S T;
COLLAPSECATEGORIES: C O L L A P S E C A T E G O R I E S;
DONTCOLLAPSECATEGORIES : D O N T C O L L A P S E C A T E G O R I E S;
EDITCONST    : E D I T C O N S T;
EDITFIXEDSIZE: E D I T F I X E D S I Z E;
INTERP       : I N T E R P;
ABSTRACT     : A B S T R A C T;
NOIMPORT     : N O I M P O R T;
NATIVE       : N A T I V E;
NATIVEREPLICATION : N A T I V E R E P L I C A T I O N;
//Editor. Indicates that this class can be created in UnrealEd and placed into a level, UI scene, or kismet window (depending on the class type). This flag is propagated to all child classes; child classes can override this flag using the NotPlaceable keyword.
PLACEABLE     : P L A C E A B L E;
//Editor. Negates a Placeable keyword inherited from a base class. Indicates that this class may not be placed into a level, etc. in UnrealEd.
NOTPLACEABLE     : N O T P L A C E A B L E;
NONTRANSACTIONAL: N O N T R A N S A C T I O N A L;
POINTER      : P O I N T E R;
SKIP         : S K I P;
NOEXPORTHEADER : N O E X P O R T H E A D E R;
HIDECATEGORIES : H I D E C A T E G O R I E S;
HIDEDROPDOWN : H I D E D R O P D O W N;
EDITHIDE     : E D I T H I D E;
EXPORT       : E X P O R T;
FINAL        : F I N A L;
WITHIN       : W I T H I N;
ITERATOR     : I T E R A T O R;
IMPLEMENTS   : I M P L E M E N T S;
LATENT       : L A T E N T;
SIMULATED    : S I M U L A T E D;
IGNORES      : I G N O R E S;
SINGULAR     : S I N G U L A R;
AUTO         : A U T O;
STATIC       : S T A T I C;
EXEC         : E X E C;
EDITINLINE   : E D I T I N L I N E;
DEPRECATED   : D E P R E C A T E D;
DATABINDING  : D A T A B I N D I N G;
EDFINDABLE   : E D F I N D A B L E;
EDITORONLY   : E D I T O R O N L Y;
EDITINLINEUSE : E D I T I N L I N E U S E;
EDITINLINENEW : E D I T I N L I N E N E W;
OUT          : O U T;
PRIVATE      : P R I V A T E;
PROTECTED    : P R O T E C T E D;
PUBLIC : P U B L I C;
PROTECTEDWRITE: P R O T E C T E D W R I T E;
LOCAL        : L O C A L;
NOTFORCONSOLE : N O T F O R C O N S O L E;
STRUCTDEFAULTPROPERTIES : S T R U C T D E F A U L T P R O P E R T I E S;
DEFAULTPROPERTIES : D E F A U L T P R O P E R T I E S;
OPERATOR     : O P E R A T O R;
PREOPERATOR  : P R E O P E R A T O R;
POSTOPERATOR : P O S T O P E R A T O R;
TRANSIENT    : T R A N S I E N T;
TRAVEL       : T R A V E L;
OPTIONAL     : O P T I O N A L;    
REPRETRY     : R E P R E T R Y;
GLOBALCONFIG : G L O B A L C O N F I G;
INPUT        : I N P U T;
INIT         : I N I T;
INSTANCED    : I N S T A N C E D;
INHERITS     : I N H E R I T S;
LOCALIZED    : L O C A L I Z E D;
DEPENDSON    : D E P E N D S O N;
COERCE       : C O E R C E;
REPNOTIFY    : R E P N O T I F Y;
REPLICATION  : R E P L I C A T I O N;
RETURN       : R E T U R N;
SUPER        : S U P E R;
THIS         : T H I S;
GLOBAL       : G L O B A L;
DEFAULT      : D E F A U L T;
UNRELIABLE   : U N R E L I A B L E;
RELIABLE     : R E L I A B L E;             
CLIENT       : C L I E N T;  
SERVER       : S E R V E R;
NOEXPORT     : N O E X P O R T;
SAFEREPLACE  : S A F E R E P L A C E;
NOCLEAR      : N O C L E A R;
VIRTUAL      : V I R T U A L;
VAR          : V A R;
BYTE         : B Y T E;
INT          : I N T;
FLOAT        : F L O A T;
BOOL         : B O O L;
STRING       : S T R I N G;
STRUCT       : S T R U C T;
ENUM         : E N U M;
NAME         : N A M E;
K2CALL       : K '2' C A L L;
K2PURE       : K '2' P U R E;
FOR          : F O R;
DO           : D O;
UNTIL        : U N T I L;
FOREACH      : F O R E A C H;
WHILE        : W H I L E;
BREAK        : B R E A K;
SWITCH       : S W I T C H;
CASE         : C A S E;
NEW          : N E W;
IF           : I F;
ELSE         : E L S E ;
ENDIF        : E N D I F;
BEGIN        : B E G I N;
END          : E N D;

//=============================================================================
// CPP
//=============================================================================
CPPTEXT       : C P P T E X T;
STRUCTCPPTEXT : S T R U C T C P P T E X T;



fragment IDENTIFIER_START :  '_' | ALPHA;
fragment IDENTIFIER_PART :   IDENTIFIER_START | DIGIT;

//=============================================================================
// Separators 
//=============================================================================
COMMA        : ',';
SEMICOLON    : ';';
COLON        : ':';
LCBRACK      : '{';
RCBRACK      : '}';
LBRACK       : '(';
RBRACK       : ')';
SQUOTE       : '\'';
DQUOTE       : '\"';
LSBRACK      : '[';
RSBRACK      : ']';
LABRACK      : '<';
RABRACK      : '>';

//=============================================================================
// Operators
//=============================================================================
preOperator  returns [int kind =0]
  : '!' 
  | '~' {kind = US_AST_BIT_NEGATION_OPERATOR;} 
  | '-' {kind = US_AST_NEGATION_OPERATOR;}
  | '++' {kind = US_AST_INC_OPERATOR;}
  | '--' {kind = US_AST_DEC_OPERATOR;}
  | '+' //TODO
  ;
postOperator returns [int kind =0]
  : '++' {kind = US_AST_INC_OPERATOR;}
  | '--' {kind = US_AST_DEC_OPERATOR;}
  ;
  
operator : assignmentOp | boolOp | shiftOp | aditiveOp | multiplicOp;

assignmentOp  returns [int kind =0] 
  : '*=' {kind = US_AST_MULASSING_OPERATOR;}
  | '/=' {kind = US_AST_DIVASSING_OPERATOR;} 
  |  '=' {kind = US_AST_ASSING_OPERATOR;} 
  | '+=' {kind = US_AST_PLUSASSING_OPERATOR;}
  | '-=' {kind = US_AST_MINUSASSING_OPERATOR;}
  | '@=' {kind = US_AST_CONCATASSING_OPERATOR;}
  | '$=' {kind = US_AST_BUKSASSING_OPERATOR;}
  ;
  
boolOp    returns [int kind =0] 
  : '==' {kind = US_AST_EQUALITY_OPERATOR;}
  | '!=' {kind = US_AST_INEQUALITY_OPERATOR;}
  | '||' {kind = US_AST_OR_OPERATOR;}
  | '&&' {kind = US_AST_AND_OPERATOR;}
  | '^^' {kind = US_AST_XOR_OPERATOR;}
  | '&'  {kind = US_AST_BIT_AND_OPERATOR;}
  | '|'  {kind = US_AST_BIT_OR_OPERATOR;}
  | '^'  {kind = US_AST_BIT_XOR_OPERATOR;}
  | '<'  {kind = US_AST_LESSTHAN_OPERATOR;}
  | '<=' {kind = US_AST_LESSTHANOREQUAL_OPERATOR;}
  | '>'  {kind = US_AST_GREATERTHAN_OPERATOR;}
  | '>=' {kind = US_AST_GREATERTHANOREQUAL_OPERATOR;}
  | '~=' {kind = US_AST_APROXIMATE_EQUALITY_OPERATOR;}
  | CLOCKWISEFROM;
  
shiftOp       returns [int kind =0]
  : '<<' {kind = US_AST_LEFT_SHIFT_OPERATOR;}
  | '>>' {kind = US_AST_RIGHT_SHIFT_OPERATOR;}
  | '>>>' {kind = US_AST_RIGHT2_SHIFT_OPERATOR;}
  ;

aditiveOp  returns [int kind =0] 
  : '+' {kind = US_AST_ADD_OPERATOR;}
  | '-' {kind = US_AST_SUBS_OPERATOR;}
  | '$' {kind = US_AST_INC_OPERATOR;}
  | '@' {kind = US_AST_DEC_OPERATOR;}
  ;

multiplicOp returns [int kind =0]
  : 
    '*'   {kind = US_AST_MUL_OPERATOR;}
  | '**'  {kind = US_AST_POWER_OPERATOR;}
  | '/' {kind = US_AST_DIV_OPERATOR;}
  | '%' {kind = US_AST_MOD_OPERATOR;}
  | DOT {kind = US_AST_DOT_OPERATOR;}
  | CROSS {kind = US_AST_CROSS_OPERATOR;}
  | CLOCKWISEFROM {kind = US_AST_CLOCKWISEFROM_OPERATOR;}
  ;


DOT : D O T;
CROSS: C R O S S;
CLOCKWISEFROM: C L O C K W I S E F R O M;
//=============================================================================
// FRAGMENTS
//=============================================================================

fragment HEX_PREFIX : '0x';
fragment DIGIT : '0'..'9';
fragment ALPHA : 'a'..'z' | 'A'..'Z';
fragment HEX_DIGIT : DIGIT | 'a'..'f' | 'A'..'F';
fragment PRINTABLE : ~( '\\' | '"' | '\r' | '\n');


IDENTIFIER : IDENTIFIER_START IDENTIFIER_PART*;

//=============================================================================
// Whitespace
//=============================================================================
WS : (' ' | '\t' | '\n' | '\r')+ {$channel=HIDDEN;};

//=============================================================================
// Multiline comment
//=============================================================================
ML_COMMENT :   '/*' (options {greedy=false;} : .)* '*/' {$channel=HIDDEN;};

//=============================================================================
// Single comment
//=============================================================================
COMMENT : '//' ~( '\n' | '\r' )* ('\r\n'| '\r'| '\n') { $channel = HIDDEN;  }
        | '//' ~( '\n' | '\r' )* { $channel = HIDDEN;  };

